#ifndef _DATReader_H_
#define _DATReader_H_

#include "DATReader.h"
#include "DATObject.h"
#include "time.h"
#include <sstream>
#include "ConvTools.h"
#include <string>

using namespace std;
using namespace cv;

void dottedLine(Mat& inImg,cv::Point inPt1, cv::Point inPt2,cv::Scalar inColor, int inThickness, int inLenghOfDots, int inLineType, bool inLeftToRight)
{
	cv::LineIterator lIterator( inImg, inPt1, inPt2, inLineType, inLeftToRight );
	int lCount = lIterator.count;
	int offset,x,y;


	for( int i = 0; i < lCount; i= i + (inLenghOfDots*2-1) )
	{
		if(i+inLenghOfDots > lCount)
			break;

		offset = lIterator.ptr - (uchar*)(inImg.data);
        y = offset/inImg.step;
		x = (offset - y*inImg.step)/(3*sizeof(uchar) /* size of pixel */);

		cv::Point lTemp1(x,y);
		for(int j=0;j<inLenghOfDots-1;j++)	//I want to know have the last of these in the iterator
			lIterator++;

        offset = lIterator.ptr - (uchar*)(inImg.data);
        y = offset/inImg.step;
        x = (offset - y*inImg.step)/(3*sizeof(uchar) /* size of pixel */);

		cv::Point lTemp2 = cvPoint(x,y);
		cv::line(inImg,lTemp1,lTemp2,inColor,inThickness,inLineType);
		for(int j=0;j<inLenghOfDots;j++)
			lIterator++;
	}
}

void dottedRect(Mat& inImg,cv::Point inPt1, cv::Point inPt2,cv::Scalar inColor, int inThickness, int inLenghOfDots, int inLineType)
{	//1---2
	//|	  |
	//4---3
	cv::Point lTempPt1(inPt2.x,inPt1.y);
	cv::Point lTempPt2(inPt1.x,inPt2.y);
	dottedLine(inImg,inPt1,lTempPt1,inColor,inThickness,inLenghOfDots,inLineType, true);
	dottedLine(inImg,lTempPt1,inPt2,inColor,inThickness,inLenghOfDots,inLineType, true);
	dottedLine(inImg,inPt2,lTempPt2,inColor,inThickness,inLenghOfDots,inLineType, false);
	dottedLine(inImg,lTempPt2,inPt1,inColor,inThickness,inLenghOfDots,inLineType, false);
}

void dottedRect(Mat& inImg,cv::Rect inRect,cv::Scalar inColor, int inThickness, int inLenghOfDots, int inLineType)
{	//1---2
	//|	  |
	//4---3
	cv::Point lPt1 = cv::Point(inRect.x,inRect.y);
	cv::Point lPt2 = cv::Point(inRect.x+inRect.width,inRect.y);
	cv::Point lPt3 = cv::Point(inRect.x+inRect.width,inRect.y+inRect.height);
	cv::Point lPt4 = cv::Point(inRect.x,inRect.y+inRect.height);

	dottedLine(inImg,lPt1,lPt2,inColor,inThickness,inLenghOfDots,inLineType, true);
	dottedLine(inImg,lPt2,lPt3,inColor,inThickness,inLenghOfDots,inLineType, true);
	dottedLine(inImg,lPt3,lPt4,inColor,inThickness,inLenghOfDots,inLineType, false);
	dottedLine(inImg,lPt4,lPt1,inColor,inThickness,inLenghOfDots,inLineType, false);
}

bool DATReader::loadXMLFile(std::string inFilename)
{
	mDATDoc = new TiXmlDocument(inFilename.c_str());
	bool lLoadOkay = mDATDoc->LoadFile();

	if(lLoadOkay)
		return checkFile();
	else
		return false;
}

bool DATReader::loadVideoFeed(std::string inFilename)
{
	mVideoFeed = new CvVision;
	return mVideoFeed->captureAVI(inFilename);
}

bool DATReader::checkFile()
{
	//later
	return true;
}

bool DATReader::playXML(int inSave, bool inInteractive, bool inReadAll, bool inShowUndead)	//Blocking function if "inInteractive" is true
{
	if(!mVideoFeed || !mDATDoc)
		return false;

	bool lStop = false;
	bool lOk = true;
	bool lFoundIt = false;
	bool lVerbose = false;
	bool lSaveObjects = false;
	bool lShowObjects = true;
	bool lShowDetection = true;
	bool lShowTracked = true;
	bool lShowDetectionAndTracked = true;
	int lFrame = mStartingFrame;
	unsigned int lThickness = 1;
	
	std::string lFilename = mDATDoc->Value();
	std::string lDirname = mDATDoc->Value();
	std::stringstream lTemp;
	time_t lTime = time(0);
	tm* lTimeLocal = localtime(&lTime);
	lTemp << "_" << lTimeLocal->tm_hour << "h"<<lTimeLocal->tm_min;
	VideoWriter lVideoSequence;
	if(inSave>=0)
	{
		if(inShowUndead)
			lFilename+="_WUndead";
		lFilename += lTemp.str()+".avi";

		//or use CV_FOURCC_DEFAULT
		lVideoSequence.open(lFilename.c_str(),inSave,mVideoFeed->getFPS(),cv::Size(mVideoFeed->getWidth(),mVideoFeed->getHeight()));
		if(!lVideoSequence.isOpened())
		{
			cerr<<"Error in VideoWritter creation"<<endl;
			return false;
		}
	}
	else if(inSave == -1)
	{
		lDirname += lTemp.str();		
#ifdef WIN32
		mkdir(lDirname.c_str());
#else
		mkdir(lDirname.c_str(),0777);
#endif
		lFilename = lFilename.substr(lFilename.find_last_of("/"));
	}
	
	//XML FORMAT (for reader)		--> There's a "FrameVal" for each frame of the Vidfiles ? (not sure)
	//<Frame value="FrameVal">
	//	<ID value="IDVal">
	//		<Position x="X" y="Y" />
	//		<Size width="W" height="H" />
	//		<DoT type="0/1" /> (for the moment : 0 --> not tracked, 1 --> Tracked)		01-->Detected	10--> Tracked	11-->Detected AND Tracked 00--> neither (could be an option)
	//		<LastDetection object="Front/Rear/LSide/RSide" frame="PossibleOtherFrameVal" />
	//	</ID>
	//</Frame>

	TiXmlNode* lRoot = mDATDoc->RootElement();
	TiXmlElement* lActualElement = lRoot->FirstChildElement("Frame");
	for( ; lActualElement ; lActualElement=lActualElement->NextSiblingElement())
	{
		if(conv2Num<unsigned int>(lActualElement->Attribute("value"))==lFrame)
		{
			lFoundIt = true;
			break;
		}
	}
	if(!lFoundIt)
	{
		std::cerr<<"No such frame number in XML file : "<<lFrame<<std::endl;
		return false;
	}

	namedWindow(mDATDoc->Value());
	Mat lShowedImage;
	if(inInteractive)
	{
		std::cout<<"a : Go backward by one frame"<<std::endl;
		std::cout<<"d : Go forward by one frame"<<std::endl;
		std::cout<<"q : Go backward by 10 frame"<<std::endl;
		std::cout<<"e : Go forward by 10 frame"<<std::endl;
		std::cout<<"s : Save current frame (in png)"<<std::endl;
		std::cout<<"S : Start recording video of current and future frame"<<std::endl;
		std::cout<<"o : Save objects only (in png)"<<std::endl;
		std::cout<<"p : Play sequence (1 FPS)"<<std::endl;
		std::cout<<"z : Show objects (default: true)"<<std::endl;
		std::cout<<"v : Verbose (write objects position) (default: false)"<<std::endl;
		std::cout<<"b : Bigger boxes and ID"<<std::endl;
		std::cout<<"n : Smaller boxes and ID"<<std::endl;
		std::cout<<"j : Show detections (default: true)"<<std::endl;
		std::cout<<"k : Show tracked objects (default: true)"<<std::endl;
		std::cout<<"l : Show detected and tracked objects (default: true)"<<std::endl;
	}
	while(!lStop)
	{
		lOk = mVideoFeed->retrieveImage(lFrame);
		cout<<"Frame "<<lFrame<<endl;
		if(!lOk)
		{
			std::cerr<<"Damn it, retrieveImage got a problem."<<std::endl;
			return false;
		}
		lShowedImage = mVideoFeed->getImage().clone();

		//reading && applying
		if(lActualElement && lShowObjects)
		{
			//Read an object and show the DaT on the lShowedImage
			DoT eType = Nothing;
			cv::Point lPt1;
			cv::Point lPt2;

			for(TiXmlElement* lObjectElement = lActualElement->FirstChildElement("DATObject"); lObjectElement ; lObjectElement = lObjectElement->NextSiblingElement("DATObject"))
			{
				DATObject lObject;
				lObject.mID = conv2Num<unsigned int>(lObjectElement->Attribute("ID"));
				
				lObject.mLastPosition.x = conv2Num<int>(lObjectElement->FirstChildElement("Position")->Attribute("x"));
				lObject.mLastPosition.y = conv2Num<int>(lObjectElement->FirstChildElement("Position")->Attribute("y"));
				lObject.mLastPosition.width = conv2Num<int>(lObjectElement->FirstChildElement("Size")->Attribute("width"));
				lObject.mLastPosition.height = conv2Num<int>(lObjectElement->FirstChildElement("Size")->Attribute("height"));

				lObject.mLastObjectType = conv2Str(lObjectElement->FirstChildElement("LastDetection")->Attribute("object"));

				eType = (DoT)conv2Num<int>(lObjectElement->FirstChildElement("DoT")->Attribute("type"));	//Not sure
				int lLastFrameDetected =  conv2Num<int>(lObjectElement->FirstChildElement("LastDetection")->Attribute("frame"));	//Could be -1

				lPt1.x = lObject.mLastPosition.x;
				lPt2.x = lObject.mLastPosition.x+lObject.mLastPosition.width;
				lPt1.y = lObject.mLastPosition.y;
				lPt2.y = lObject.mLastPosition.y+lObject.mLastPosition.height;

				if(inShowUndead && eType==DeadAllAlong)
				{
					rectangle(lShowedImage,lPt1,lPt2,mColors[lObject.mLastObjectType],lThickness);
					
					cv::Point lPtText = lPt1;
					lPtText.y = lPtText.y - 1;
					putText (lShowedImage,"Undead",lPtText, FONT_HERSHEY_SIMPLEX, 0.5, mColors[lObject.mLastObjectType]);
				}

				//Draw groundtruth
				if(eType==GroundTruth)
				{
					rectangle(lShowedImage,lPt1,lPt2,mColors[lObject.mLastObjectType],lThickness*2);
				}

				//Draw detection
				if((eType==Detected && lShowDetection) || (eType==DetectedAndTracked && lShowDetectionAndTracked))
				{
					rectangle(lShowedImage,lPt1,lPt2,mColors[lObject.mLastObjectType],lThickness);
				}

				//Draw tracking
				if((eType==Tracked && lShowTracked) || (eType==DetectedAndTracked && lShowDetectionAndTracked))
				{				
					lPt1.x = lPt1.x - lThickness ;
					lPt2.x = lPt2.x + lThickness;
					lPt1.y = lPt1.y - lThickness;
					lPt2.y = lPt2.y + lThickness;

					dottedRect(lShowedImage,lPt1,lPt2,mColors[lObject.mLastObjectType],lThickness,3,8);
				}

				//Write ID
				if((inShowUndead || eType!=DeadAllAlong))
				{
					lPt1.x = lPt1.x + lObject.mLastPosition.width/2 + 1;
					lPt1.y = lPt1.y + lObject.mLastPosition.height/2 + 1;
					if((eType==Tracked && lShowTracked) || (eType==Detected && lShowDetection) || eType==DetectedAndTracked || eType==GroundTruth)
						putText (lShowedImage,conv2Str(lObject.mID).c_str(),lPt1, FONT_HERSHEY_SIMPLEX, 0.5, mColors[lObject.mLastObjectType]);
				}
				

				if(lVerbose)
				{
					cout<<"ID "<<lObject.mID<<" : x="<<lObject.mLastPosition.x<<" y="<<lObject.mLastPosition.y<<" w="<<lObject.mLastPosition.width<<" h="<<lObject.mLastPosition.height<<endl;
				}
				if(lSaveObjects)
				{
					string lXMLName = mDATDoc->Value();
					stringstream lTemp;
					lTemp << "Frame-" << lFrame << "_ID-"<<lObject.mID;
					string lObjectFilename = lXMLName+"_"+lTemp.str()+".png";

					imwrite(lObjectFilename.c_str(),cv::Mat(mVideoFeed->getImage(), lObject.mLastPosition));

				}
			}
		}
		if(lSaveObjects)lSaveObjects=false;

		//showing
		imshow(mDATDoc->Value(),lShowedImage);

		//Add to the video
		if(inSave>-1)
			lVideoSequence.write(lShowedImage);
		else if(inSave ==-1)
		{
			
			std::string lFrameFilename = lDirname +lFilename + "_" + conv2Str(lFrame) + ".png";
			imwrite(lFrameFilename.c_str(),lShowedImage);
		}

		//what do we do next ?
		char lKey;
		if(inInteractive)
		{
			string lXMLName = mDATDoc->Value();
			stringstream lTemp;
			lTemp << lFrame;
			string lFilename = lXMLName+"_"+lTemp.str()+".png";

			lKey = waitKey(0);
			switch(lKey)
			{
			case 'd':
				if(lFrame<mVideoFeed->getNumFrames()-1)
				{
					lFrame++;
					lActualElement=lActualElement->NextSiblingElement("Frame");	
				}
				break;
			case 'a':
				if(lFrame>0)
				{
					lFrame--;
					lActualElement=(TiXmlElement*)lActualElement->PreviousSibling("Frame"); //Not an element... could fuck things over
				}
				break;
			case 'e':
				if(lFrame+10<mVideoFeed->getNumFrames())
				{
					for(unsigned int i = 0 ; lActualElement && i<10 ; i++)
						lActualElement=lActualElement->NextSiblingElement("Frame");
					lFrame = lFrame+10;
				}
				break;
			case 'q':
				if(lFrame-10>0)
				{
					for(unsigned int i = 0 ; lActualElement && i<10 ; i++)
						lActualElement=(TiXmlElement*)lActualElement->PreviousSibling("Frame"); //Not an element... could fuck things over
					lFrame = lFrame-10;
				}
				break;
			case 's':
				//save image
				imwrite(lFilename.c_str(),lShowedImage);
				break;
			case 'o':
				//save objects
				lSaveObjects = !lSaveObjects;
				break;
			case 'z':
				//Show object
				lShowObjects = !lShowObjects;
				break;
			case 'b': //BIGGER !!!
				lThickness++;
				changeFont(CV_FONT_HERSHEY_SIMPLEX,0.5,0.5,0,lThickness);
				break;
			case 'n' ://smaller :(
				if(lThickness>1)
					lThickness--;
				changeFont(CV_FONT_HERSHEY_SIMPLEX,0.5,0.5,0,lThickness);
				break;
			case 'v' ://talk !
				lVerbose = !lVerbose;
			break;
			case 'j': //don't show detection
				lShowDetection = !lShowDetection;
			break;
			case 'k'://don't show tracked
				lShowTracked = !lShowTracked;
			break;
			case 'l'://don't show Show Detection And Tracked
				lShowDetectionAndTracked = !lShowDetectionAndTracked;
			break;
			case 27:
				lStop = true;
				break;
			};
		}
		else
		{
			//++
			lFrame++;
			lActualElement=lActualElement->NextSiblingElement("Frame");	
		}

		//Going throught the frames
		if(lActualElement)
		{
			if(conv2Num<unsigned int>(lActualElement->Attribute("value"))==lFrame)
				lFoundIt = true;
			else	//there's no info on this frame (but there's still frames) (should NOT hapened)
			{
				//lStop = true;
				//lFoundIt=false;
				std::cerr<<"Desynchronisation of XML and VidFiles."<<std::endl;
				return false;
			}
		}
		else
		{
			//No more frame
			lFoundIt=false;	//Not sure
			if(lFrame<0)
			{
				lFrame = 0;
				lActualElement = lRoot->FirstChildElement("Frame");
			}
			else
			{
				lFrame = mVideoFeed->getNumFrames();
				lActualElement = (TiXmlElement*)lRoot->LastChild("Frame"); //Not an element... could fuck things over
			}
			if(conv2Num<unsigned int>(lActualElement->Attribute("value"))==lFrame)
				lFoundIt = true;	//try to sync with the last or first frame
			if(!inInteractive)
				lStop=true; //to get out of the loop without "ESC" key
		}
	}
	//Close the VideoWriter
	
	destroyWindow(mDATDoc->Value());
	return true;
}

#endif
